
#include "filemanager.hpp"

/*########################################################################################################

  Funcions de la classe fileManager

  La clase fileManager s'encarrega de l'organització de fitxers.

  ######################################################################################################*/

fileManager::fileManager(char* disk)
{
    _dirman = new directoryManager (disk);
}

fileManager::~fileManager() {}

void fileManager::crearf(const string &nomf, const int &tipus, const unsigned short &mr, const unsigned short &mcc)
{
    unsigned short fs = _dirman -> getFreeSlot();

    if (fs == 0) {
        cout << "ERROR CREARF(NOM=" << nomf << "): ";
        cout << "Directory ple." << endl;
   } else {
        directoryManager::slot s;
        s = _dirman -> getSlot(nomf);
        if (s.fileName != "") {
            cout << "ERROR CREARF(NOM=" << nomf << "): ";
            cout << "L'arxiu ja existeix." << endl;
        } else {
            s.fileName = nomf;
            s.firstPag = 0;
            s.tipus = tipus;
            s.mr = mr;
            s.mcc = mcc;
            s.slotNum = fs;

            _dirman -> setSlot(s);
            _dirman -> setFreeSlot(fs + 14);

            cout << "CREARF(NOM=" << nomf << " TIPUS=" << tipus << " MR=" << mr << " MCC=" << mcc << "): ";
            cout << "SLOT=" << fs << endl;
        }
    }
}

void fileManager::delf(const string &nomf)
{
    directoryManager::slot s = _dirman -> getSlot(nomf);
    if (s.fileName == "")
        cout << "ERROR DELF(" << nomf << "): L'arxiu no existeix." << endl;
    else
    {
        cout << "DELF(" << nomf << "): ";
        unsigned short pagTmp = 0;
        unsigned short pagVal = 0;
        pagTmp = pagVal = s.firstPag;
        while(_dirman -> getNextPagVal(pagVal) != 0) {
            pagTmp = _dirman -> getNextPagVal(pagVal);
            _dirman -> setNextPagVal(pagVal, 0);
            _dirman -> setFreePag(pagVal);
            cout << " " << pagVal;
            pagVal = pagTmp;
        }

        if (pagTmp != 0) {
            _dirman -> setNextPagVal(pagTmp, 0);
            _dirman -> setFreePag(pagTmp);
            cout << " " << pagTmp;
        }

        _dirman -> deleteSlot(s.slotNum);
        cout << endl;
    }
}

void fileManager::insertr(const string &nomf, const string &key, const string &altres)
{
    directoryManager::slot s = _dirman -> getSlot(nomf);
    if (s.fileName == "") {
        cout << "ERROR INSERTR(NOM=" << nomf << "): ";
        cout << "L'arxiu no existeix." << endl;
    } else if (existFulla(nomf, key).first > 0) {
        cout << "ERROR INSERTR(NOM=" << nomf << " KEY=" << key << "): ";
        cout << "Ja existeix una clau amb aquest valor." << endl;
    } else {

        fulla f;
        f.key = key;
        f.data = altres;
        f.fitxer = s;

        if (s.firstPag == 0) {
            // No hi ha cap pagina, en creem una.
            int fp = _dirman -> getFreePag();
            if (fp == -1) {
                cout << "ERROR INSERTR(NOM=" << nomf << " KEY=" << key << "): ";
                cout << "Disc ple." << endl;
            } else {

                // Actualitzem el fitxer, per tal que faci referencia a la pagina.
                s.firstPag = fp;
                _dirman -> updateSlot(s);
                _dirman -> setUsedPag(fp);
                
                cout << "INSERTR(NOM=" << nomf << " KEY=" << key << "): PAG=" << fp << " POS=" << 6 << endl;
                cout << "DATA=" << altres << endl;

                if ((f.key.length() == (unsigned int) s.mcc) && (f.data.length() <= (unsigned int) (s.mr-s.mcc))) {
                    f.pesq = 6;
                    f.pdret = 0;
                    f.data.resize(s.mr-s.mcc,' ');
                    f.location = posicio(fp,6);
                    setFulla(f);
                    setFirstFulla (fp, 6);
                    setLastFulla(fp, 6);
                }
            }

        } else {
            
            
            // Hi ha almenys una pagina, mirem si entra un registe més...
            if (getFirstFreeSlot(s.firstPag,f) == -1) {
                
                // No entra, mirem si hi ha altres pagines i busquem si hi cap el registre.
                bool found = false;
                unsigned short pagVal = s.firstPag;
                int slotPos = 0;
                while(_dirman -> getNextPagVal(pagVal) != 0 && !found) {
                    pagVal = _dirman -> getNextPagVal(pagVal);
                    slotPos = getFirstFreeSlot(pagVal,f);
                    found = (slotPos != -1);
                }
                
                // Creem el registre a la pagina que hem trobat.
                if (found) {
                    
                    //Falta: Hem de buscar el lloc on li toca anar i reordenar si fos necessari...
                    f.pesq = getLastFulla(pagVal);
                    f.pdret = 0;
                    f.data.resize(s.mr-s.mcc,' ');
                    f.location = posicio(pagVal,slotPos);
                    setFulla(f);

                    // Actualitzem el punter de la última fulla
                    char* _pagCtrl = _dirman -> _buffman -> query(pagVal);
                    _pagCtrl[getLastFulla(pagVal)+2+s.mr] = (char) ((slotPos>>8) & 0XFF);
                    _pagCtrl[getLastFulla(pagVal)+2+s.mr+1] = (char) (slotPos & 0XFF);
                    _dirman -> _buffman -> update(pagVal);

                    cout << "INSERTR(NOM=" << nomf << " KEY=" << key << ") PAG=" << pagVal << " POS=";
                    cout << slotPos << endl << "DATA=" << altres << endl;

                    // Actualitzem el punter de pagina de la ultima fulla
                    setLastFulla(pagVal, slotPos);
                    
                } else {
                    
                    // Provem a crear una pagina nova...
                    int fp = _dirman -> getFreePag();
                    if (fp == -1) {
                        cout << "ERROR INSERTR(NOM=" << nomf << " KEY=" << key << "): ";
                        cout << "Disc ple." << endl;
                    } else {

                        // Actualitzem el punter de la darrera pagina del fitxer.
                        _dirman -> setNextPagVal(pagVal, fp);
                        _dirman -> setUsedPag(fp);
                        
                        cout << "INSERTR(NOM=" << nomf << " KEY=" << key << "): PAG=" << fp << " POS=" << 6 << endl;
                        cout << "DATA=" << altres << endl;

                        if ((f.key.length() == (unsigned int) s.mcc) && (f.data.length() <= (unsigned int) (s.mr-s.mcc))) {
                            f.pesq = 6;
                            f.pdret = 0;
                            f.data.resize(s.mr-s.mcc,' ');
                            f.location = posicio(fp,6);
                            setFulla(f);
                            setFirstFulla (fp, 6);
                            setLastFulla(fp, 6);
                        }
                    }
                }
                
            } else {
                // Sí que entra, l'afegim...
                //Falta: Hem de buscar el lloc on li toca anar i reordenar si fos necessari...
                int slotPos = getFirstFreeSlot(s.firstPag,f);
                f.pesq = getLastFulla(s.firstPag);
                f.pdret = 0;
                f.data.resize(s.mr-s.mcc,' ');
                f.location = posicio(s.firstPag,slotPos);
                setFulla(f);

                // Actualitzem el punter de la última fulla
                char* _pagCtrl = _dirman -> _buffman -> query(s.firstPag);
                _pagCtrl[getLastFulla(s.firstPag)+2+s.mr] = (char) ((slotPos>>8) & 0XFF);
                _pagCtrl[getLastFulla(s.firstPag)+2+s.mr+1] = (char) (slotPos & 0XFF);
                _dirman -> _buffman -> update(s.firstPag);

                cout << "INSERTR(NOM=" << nomf << " KEY=" << key << ") PAG=" << s.firstPag << " POS=";
                cout << slotPos << endl << "DATA=" << altres << endl;

                // Actualitzem el punter de pagina de la ultima fulla
                setLastFulla(s.firstPag, slotPos);
            }
        }
    }
}

void fileManager::delr(const string &nomf, const string &key)
{
    directoryManager::slot s = _dirman -> getSlot(nomf);
    if (s.fileName == "")
        cout <<  "ERROR DELR(NOM=" << nomf << " KEY=" << key << "): L'arxiu no existeix." << endl;
    else
    {
        // Mirem si trobem el registre...
        posicio fdelete = existFulla(nomf, key);
        
        // Si el trobem, l'esborrem
        if (fdelete.first > 0) {
            int x, z;
            char* _pagCtrl = _dirman -> _buffman -> query(fdelete.first);
            for (z = fdelete.second; z < (fdelete.second+2+s.mr+2); z++)
                for (x = 0; x < 8; x++)
                    _pagCtrl[z] &= ~(1 << x);
            _dirman -> _buffman -> update(fdelete.first);
            cout << "DELR(NOM=" << nomf << " KEY=" << key << "): PAG=" << fdelete.first << " POS=" << fdelete.second << endl;
        } else {
            cout <<  "ERROR DELR(NOM=" << nomf << " KEY=" << key << "): El registre no existeix." << endl;
        }
        
    }
}

void fileManager::query(const string &nomf, const int &tipus, const string &key_ini, const string &key_fin)
{
    if (nomf == "#map") {
        cout << "QUERY(#map): " << endl;
	    utils::mapArray (_dirman -> _pagCtrl, 512, BIN);
    } else if (nomf == "#all") {

        cout << "QUERY(#all): " << endl;
        unsigned short sl;
        for (sl = 520; sl <= 1012; sl += 14) {
            directoryManager::slot s = _dirman -> getSlot(sl);
            if (s.fileName != "") query(s.fileName, tipus, key_ini, key_fin);
        }
    } else {
        directoryManager::slot s = _dirman -> getSlot(nomf);
        if (s.fileName == "")
            cout <<  "ERROR QUERY(NOM=" << nomf << "): L'arxiu no existeix." << endl;
        else {
            cout << "QUERY(NOM=" << nomf << " KEY=" << key_ini << " KEY=" << key_fin << "):" << endl;;
            unsigned short pagVal = s.firstPag;

            // Revisamos todas las paginas...
            while(_dirman -> getNextPagVal(pagVal) != 0) {

                unsigned short sl;
                for (sl = 6; sl <= getLastFulla(pagVal); sl += 2+s.mr+2) {
                    fulla f = getFulla(pagVal, sl, s.mcc, s.mr-s.mcc);
                    if (isValidFulla(pagVal, sl)) {
                        cout << "REG: PAG=" << pagVal << " POS=" << sl << " KEY=" << f.key << " DATA=" << f.data << endl;
                    }
                }
                pagVal = _dirman -> getNextPagVal(pagVal);
            }

            unsigned short sl;
            for (sl = 6; sl <= getLastFulla(pagVal); sl += (2+s.mr+2)) {
                fulla f = getFulla(pagVal, sl, s.mcc, s.mr-s.mcc);
                if (f.key.length() > 0) {
                    cout << "REG: PAG=" << pagVal << " POS=" << sl << " KEY=" << f.key << " DATA=" << f.data << endl;
                }
            }
        }
    }
}

// -------------------------------------------------------------------------------
// Funcions que gestionen les fulles
// -------------------------------------------------------------------------------

fileManager::fulla fileManager::getFulla(const int &pag, const int &pos, int klen, int dlen)
{
    fulla f;
    if (isValidFulla(pag, pos)) {
        char* _pagCtrl = _dirman -> _buffman -> query(pag);
        f.pesq = (int) (_pagCtrl[pos] & 0XFF) << 8;
        f.pesq += (int) _pagCtrl[pos+1] & 0XFF;
        unsigned char* bytes = (unsigned char *)&_pagCtrl[pos+2];
        f.key = string (reinterpret_cast<char const*>(bytes), klen);
        bytes = (unsigned char *)&_pagCtrl[pos+2+klen+1];

        int i;
        string s;
        f.data = "";
        for (i=0; i < dlen; i++) {
            bytes = (unsigned char *)&_pagCtrl[pos+2+klen+1+i];
            s = string (reinterpret_cast<char const*>(bytes), 1);
            if (isalnum(s[0]))
                f.data.append (s);
        }
        
        f.pdret = (int) (_pagCtrl[pos+2+klen+1+dlen+1] & 0XFF) << 8;
        f.pdret += (int) _pagCtrl[pos+2+klen+1+dlen+2] & 0XFF;
        _dirman -> _buffman -> update(pag);
        f.location = posicio(pag,pos);
    }
    return f;
}

fileManager::fulla fileManager::getFulla(const int &pag, const string &key, const int &klen, const int &dlen)
{
    fulla ret;
    bool found = false;
    unsigned short sl;
    for (sl = 6; sl <= 1024-(2+klen+dlen+2) && !found; sl += 2+klen+dlen+2) {
        fulla f = getFulla(pag, sl, klen, dlen);
        if (f.key == key) {
            ret = f;
            found = true;
        }
    }
    return ret;
}

void fileManager::setFulla(const fulla &f)
{
    int pag = f.location.first;
    int pos = f.location.second;
    int klen = f.fitxer.mcc;
    int dlen = f.fitxer.mr - klen;
    
    char* _pagCtrl = _dirman -> _buffman -> query(pag);
    _pagCtrl[pos] = (char) ((f.pesq>>8) & 0XFF);
    _pagCtrl[pos+1] = (char) ((f.pesq) & 0XFF);
    unsigned char* bytes = (unsigned char *)&_pagCtrl[pos+2];
    int i;
    for (i = 0; i < klen; i++)
        bytes[i] = f.key[i];
    bytes = (unsigned char *)&_pagCtrl[pos+2+klen+1];
    for (i = 0; i < dlen; i++)
        bytes[i] = f.data[i];
    _pagCtrl[pos+2+klen+1+dlen+1] = (char) ((f.pdret>>8) & 0XFF);
    _pagCtrl[pos+2+klen+1+dlen+2] = (char) ((f.pdret) & 0XFF);
    _dirman -> _buffman -> update(pag);
}

posicio fileManager::existFulla(const string &nomf, const string &key)
{
    int i, t = 0;
    bool found = false;
    directoryManager::slot s = _dirman -> getSlot(nomf);
    unsigned short pagVal = s.firstPag;
    unsigned short foundPag = 0;
    
    while(pagVal != 0  && !found) {
        
        if (pagVal != 0) {
            for (i=6; i < 1024 && !found; i+=2+s.mr+2) {
                if (isValidFulla(pagVal, i)) {
                    fulla f = getFulla(pagVal, i, s.mcc, s.mr-s.mcc);
                    if (f.key == key) {
                        found = true;
                        foundPag = pagVal;
                        t = i;
                    }
                }
            }
        }

        if (_dirman -> getNextPagVal(pagVal) != 0)
            pagVal = _dirman -> getNextPagVal(pagVal);
        else
            pagVal = 0;
    }
    return found ? posicio(foundPag,t) : posicio(0,0);
}

bool fileManager::isValidFulla(const int &pag, const int &pos)
{
    char* _pagCtrl = _dirman -> _buffman -> query(pag);
    unsigned short i = (int) (_pagCtrl[pos] & 0XFF) << 8;
    i += (int) _pagCtrl[pos+1] & 0XFF;
    _dirman -> _buffman -> update(pag);
    return (i != 0 && i <= getLastFulla(pag));
}

unsigned short fileManager::getFirstFulla(const int &pag)
{
    char* _pagCtrl = _dirman -> _buffman -> query(pag);
    unsigned short i = (int) (_pagCtrl[2] & 0XFF) << 8;
    i += (int) _pagCtrl[3] & 0XFF;
    _dirman -> _buffman -> update(pag);
    return i;
}

void fileManager::setFirstFulla(const int &pag, const unsigned short &pos)
{
    char* _pagCtrl = _dirman -> _buffman -> query(pag);
	_pagCtrl[2] = (char) ((pos>>8) & 0XFF);
	_pagCtrl[3] = (char) ((pos) & 0XFF);
	_dirman -> _buffman -> update(pag);
}

unsigned short fileManager::getLastFulla(const int &pag)
{
    char* _pagCtrl = _dirman -> _buffman -> query(pag);
    unsigned short i = (int) (_pagCtrl[4] & 0XFF) << 8;
    i += (int) _pagCtrl[5] & 0XFF;
    _dirman -> _buffman -> update(pag);
    return i;
}

void fileManager::setLastFulla(const int &pag, const unsigned short &pos)
{
    char* _pagCtrl = _dirman -> _buffman -> query(pag);
	_pagCtrl[4] = (char) ((pos>>8) & 0XFF);
	_pagCtrl[5] = (char) ((pos) & 0XFF);
	_dirman -> _buffman -> update(pag);
}

int fileManager::getFirstFreeSlot(const int &pag, const fulla &f)
{
    int i, res = 0;
    bool found = false;
    
    for (i=6; i < 1024 && !found; i+=2+f.fitxer.mr+2) {
        if (!isValidFulla(pag, i)) {
            found = true;
            res = i;
        }
    }
    
    return found ? res : -1;
}
